home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / TURBOPASCAL WIN / OWLDEMOS.PAK / MCISOUND.PAS < prev    next >
Pascal/Delphi Source File  |  1992-06-08  |  20KB  |  699 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Turbo Pascal for Windows                        }
  4. {   Windows 3.1 MCI API Sound Support               }
  5. {               Demonstration Program               }
  6. {                                                   }
  7. {   Copyright (c) 1992 by Borland International     }
  8. {                                                   }
  9. {***************************************************}
  10.  
  11. program MCISound;
  12.  
  13. { This example demonstrates the use of MCI APIs in Windows 3.1 in an
  14.   OWL application.  You must have a sound board and its device driver
  15.   properly installed under Windows 3.1.
  16.  
  17.   You may copy one of the .WAV files from the WINDOW subdirectory in
  18.   your system to this example's subdirectory.
  19.  
  20.   Run the .EXE choose Open from the File menu and select a .WAV file.
  21.   Choose Play from the Options menu and control of the sound is done
  22.   via the Options menu and the scroll bar. The Options menu lets you
  23.   stop/play/pause and resume.  The scrollbar allows random access
  24.   through the waveform while it is playing.
  25.  
  26.   This example demostrates the use MCI API and use of a callback
  27. }
  28.  
  29. uses Strings, WinTypes, WinProcs, WObjects, WinDOS, Win31, ShellAPI,
  30.   MMSystem, CommDlg, BWCC;
  31.  
  32. {$R MCISOUND}
  33.  
  34. const
  35.  
  36. { Resource IDs }
  37.  
  38.   id_Menu  = 100;
  39.   id_About = 100;
  40.   id_Instr = 101;   { Instructions }
  41.   id_Icon  = 100;
  42.  
  43. { Menu command IDs }
  44.  
  45.   cm_FileOpen   = 201;
  46.   cm_HelpAbout  = 300;
  47.   cm_SoundPlay  = 301;
  48.   cm_SoundPause = 302;
  49.  
  50.   id_Scroll = 150;  { Scroll bar }
  51.   Timer_Id  = 264;  { Unique timer ID. }
  52.  
  53. type
  54.  
  55. { Filename string }
  56.  
  57.   TFilename = array[0..255] of Char;
  58.  
  59. { Sound Control Scroll Bar }
  60.  
  61.   PSoundBar = ^TSoundBar;
  62.   TSoundBar = object(TScrollBar)
  63.     WaveRatio  : Integer;
  64.     WaveLength : Longint;
  65.     ElementName: TFilename;
  66.  
  67.     procedure RePosAndPlay(NewPos: Longint); virtual;
  68.  
  69.     procedure ScrollSetInfo(WRatio: Integer; WLength: Longint); virtual;
  70.     procedure ScrollSetName(EName: PChar); virtual;
  71.  
  72.     procedure SBLineUp(var Msg: TMessage);
  73.       virtual nf_First + sb_LineUp;
  74.     procedure SBLineDown(var Msg: TMessage);
  75.       virtual nf_First + sb_LineDown;
  76.     procedure SBPageUp(var Msg: TMessage);
  77.       virtual nf_First + sb_PageUp;
  78.     procedure SBPageDown(var Msg: TMessage);
  79.       virtual nf_First + sb_PageDown;
  80.     procedure SBThumbPosition(var Msg: TMessage);
  81.       virtual nf_First + sb_ThumbPosition;
  82.     procedure SBTop(var Msg: TMessage);
  83.       virtual nf_First + sb_Top;
  84.     procedure SBBottom(var Msg: TMessage);
  85.       virtual nf_First + sb_Bottom;
  86.   end;
  87.  
  88. { Application main window }
  89.  
  90.   PSoundWindow = ^TSoundWindow;
  91.   TSoundWindow = object(TWindow)
  92.     ElementName: TFilename;
  93.     IsRunning  : Boolean;
  94.     Paused     : Boolean;
  95.     TimerGoing : Boolean;
  96.     WaveRatio  : Integer;
  97.     WaveLength : Longint;
  98.     SoundBar   : PSoundBar;
  99.  
  100.     MciGenParm   : TMCI_Generic_Parms;
  101.     MciOpenParm  : TMCI_Open_Parms;
  102.     MciPlayParm  : TMCI_Play_Parms;
  103.     MciStatusParm: TMCI_Status_Parms;
  104.     MciSetParm   : TMCI_Set_Parms;
  105.  
  106.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  107.     destructor  Done; virtual;
  108.  
  109.     procedure GetDeviceInfo;     virtual;
  110.     procedure StopWave;          virtual;
  111.     procedure UpdateSoundWindow; virtual;
  112.  
  113.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  114.     function  GetClassName: PChar; virtual;
  115.     procedure SetupWindow; virtual;
  116.  
  117.     procedure MciNotify(var Msg: TMessage);
  118.       virtual wm_First + mm_MCINotify;
  119.  
  120.     procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
  121.  
  122.     procedure CMFileOpen(var Msg: TMessage);
  123.       virtual cm_First + cm_FileOpen;
  124.     procedure CMSoundPlay(var Msg: TMessage);
  125.       virtual cm_First + cm_SoundPlay;
  126.     procedure CMSoundPause(var Msg: TMessage);
  127.       virtual cm_First + cm_SoundPause;
  128.     procedure CMHelpAbout(var Msg: TMessage);
  129.       virtual cm_First + cm_HelpAbout;
  130.  
  131.     procedure WMIdleStuff(var Msg: TMessage);
  132.       virtual wm_First + wm_Timer;
  133.   end;
  134.  
  135. { Application object }
  136.  
  137.   TSoundApp = object(TApplication)
  138.     procedure InitInstance;   virtual;
  139.     procedure InitMainWindow; virtual;
  140.   end;
  141.  
  142. { Initialized globals }
  143.  
  144. const
  145.   DemoTitle  : PChar   = 'MCI Sound Demo Program';
  146.   DeviceID   : Word    = 0;
  147.   FlushNotify: Boolean = FALSE;
  148.  
  149. { Global variables }
  150.  
  151. var
  152.   App: TSoundApp;
  153.  
  154.  
  155. { TSoundBar Methods }
  156.  
  157. procedure TSoundBar.RePosAndPlay(NewPos: Longint);
  158. var
  159.   MciSeekParm  : TMCI_Seek_Parms;
  160.   MciGenParm   : TMCI_Generic_Parms;
  161.   MciOpenParm  : TMCI_Open_Parms;
  162.   MciPlayParm  : TMCI_Play_Parms;
  163.   MciStatusParm: TMCI_Status_Parms;
  164.   MciSetParm   : TMCI_Set_Parms;
  165. begin
  166. { Only allow SEEK if playing. }
  167.  
  168.   if DeviceID = 0 then
  169.     Exit;
  170.  
  171. { Close the currently playing wave.
  172. }
  173.   FlushNotify := True;
  174.   MciGenParm.dwCallback := 0;
  175.   mciSendCommand(DeviceID, mci_Stop,  mci_Wait, Longint(@MciGenParm));
  176.   mciSendCommand(DeviceID, mci_Close, mci_Wait, Longint(@MciGenParm));
  177.  
  178. { Open the wave again and seek to new position.
  179. }
  180.   MciOpenParm.dwCallback := 0;
  181.   MciOpenParm.wDeviceID  := DeviceID;
  182.   MciOpenParm.wReserved0 := 0;
  183.   MciOpenParm.lpstrDeviceType := nil;
  184.   MciOpenParm.lpstrElementName:= ElementName;
  185.   MciOpenParm.lpstrAlias      := nil;
  186.  
  187.   if mciSendCommand(DeviceID, mci_Open, mci_Wait or mci_Open_Element,
  188.       Longint(@MciOpenParm)) <> 0 then
  189.     MessageBox(HWindow, 'Open Error', DemoTitle, mb_OK)
  190.   else
  191.   begin
  192.     DeviceID := MciOpenParm.wDeviceID;
  193.  
  194. { Our time scale is in SAMPLES.
  195. }
  196.     MciSetParm.dwTimeFormat := mci_Format_Samples;
  197.     if mciSendCommand(DeviceID, mci_Set, mci_Set_Time_Format,
  198.         Longint(@MciSetParm)) <> 0 then
  199.       MessageBox(HWindow, 'Set Time Error', DemoTitle, mb_OK)
  200.     else
  201.     begin
  202. { Compute new position, remember the scrollbar range has been scaled based
  203.   on waveRatio.
  204. }
  205.       MciSeekParm.dwCallback:= 0;
  206.       if (NewPos * WaveRatio) > WaveLength then
  207.         MciSeekParm.dwTo := WaveLength
  208.       else
  209.         MciSeekParm.dwTo := NewPos * WaveRatio;
  210.       
  211.       if mciSendCommand(DeviceID, mci_Seek, mci_To,
  212.           Longint(@MciSeekParm)) <> 0 then
  213.         MessageBox(HWindow, 'Seek Error', DemoTitle, mb_OK)
  214.       else
  215.       begin
  216.     MciPlayParm.dwCallback:= HWindow;
  217.     MciPlayParm.dwFrom    := 0;
  218.     MciPlayParm.dwTo      := 0;
  219.     if mciSendCommand(DeviceID, mci_Play, mci_Notify,
  220.         Longint(@MciPlayParm)) <> 0 then
  221.           MessageBox(HWindow, 'Play Error', DemoTitle, mb_OK);
  222.       end;
  223.     end;
  224.   end;  { Playing }
  225. end;
  226.  
  227. { Sets the given ratio and length as the current WaveRatio and WaveLength
  228.   of the Sound Bar.
  229. }
  230. procedure TSoundBar.ScrollSetInfo(WRatio: Integer; WLength: Longint);
  231. begin
  232.   WaveRatio  := WRatio;
  233.   WaveLength := WLength;
  234. end;
  235.  
  236. { Sets the given string as the name of the SoundBar.
  237. }
  238. procedure TSoundBar.ScrollSetName(EName: PChar);
  239. begin
  240.   StrCopy(ElementName, EName);
  241. end;
  242.  
  243. { Responds to a click on the Scroll Bar's up-arrow by stepping
  244.   the wave.  Calls on the inherited SBLineUp to do the actual
  245.   update of the scroll bar, then uses the new position for the
  246.   sound.
  247. }
  248. procedure TSoundBar.SBLineUp(var Msg: TMessage);
  249. begin
  250.   TScrollBar.SBLineUp(Msg);
  251.   RePosAndPlay(GetPosition);
  252. end;
  253.  
  254. { Responds to a click on the Scroll Bar's down-arrow as above.
  255. }
  256. procedure TSoundBar.SBLineDown(var Msg: TMessage);
  257. begin
  258.   TScrollBar.SBLineDown(Msg);
  259.   RePosAndPlay(GetPosition);
  260. end;
  261.  
  262. { Responds to a click on the Scroll Bar's page-up area as above.
  263. }
  264. procedure TSoundBar.SBPageUp(var Msg: TMessage);
  265. begin
  266.   TScrollBar.SBPageUp(Msg);
  267.   RePosAndPlay(GetPosition);
  268. end;
  269.  
  270. { Responds to a click on the Scroll Bar's page-down area as above.
  271. }
  272. procedure TSoundBar.SBPageDown(var Msg: TMessage);
  273. begin
  274.   TScrollBar.SBPageDown(Msg);
  275.   RePosAndPlay(GetPosition);
  276. end;
  277.  
  278. { Responds to a movement of the Scroll Bar's thumb as above.
  279. }
  280. procedure TSoundBar.SBThumbPosition(var Msg: TMessage);
  281. begin
  282.   TScrollBar.SBThumbPosition(Msg);
  283.   RePosAndPlay(GetPosition);
  284. end;
  285.  
  286. { Responds to movement of the scroll bar to the Top as above.
  287. }
  288. procedure TSoundBar.SBTop(var Msg: TMessage);
  289. begin
  290.   TScrollBar.SBTop(Msg);
  291.   RePosAndPlay(GetPosition);
  292. end;
  293.  
  294. { Responds to movement of the scroll bar to the Bottom as above.
  295. }
  296. procedure TSoundBar.SBBottom(var Msg: TMessage);
  297. begin
  298.   TScrollBar.SBBottom(Msg);
  299.   RePosAndPlay(GetPosition);
  300. end;
  301.  
  302.  
  303. { TSoundWindow Methods }
  304.  
  305. { Constructs an instance of the TSoundWindow, positioning it and setting
  306.   its data fields to their initial values.
  307. }
  308. constructor TSoundWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  309. begin
  310.   TWindow.Init(AParent, ATitle);
  311.  
  312.   Attr.X := 50;
  313.   Attr.Y := 100;
  314.   Attr.W := 400;
  315.   Attr.H := 150;
  316.  
  317.   IsRunning  := False;
  318.   Paused     := False;
  319.   WaveLength := 0;
  320.   WaveRatio  := 0;
  321.   StrCopy(ElementName, '');
  322.  
  323.   SoundBar := New(PSoundBar, Init(@Self, id_Scroll, 50, 50, 300, 0, True));
  324.   SoundBar^.SetRange(0, 0);
  325. end;
  326.  
  327. { Destroys an instance of the Sound Window.  Before calling the ancestral
  328.   destructor to remove the object, stops the current wave.
  329. }
  330. destructor TSoundWindow.Done;
  331. begin
  332.   StopWave;
  333.   TWindow.Done;
  334. end;
  335.  
  336.  
  337. { Repaints the window, posting information about the current sound.
  338. }
  339. procedure TSoundWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
  340. var
  341.   Buffer: array [0..100] of Char;
  342. begin
  343. { File Name }
  344.   if StrLen(ElementName) > 0 then
  345.     TextOut(PaintDC, 5, 5, ElementName, StrLen(ElementName))
  346.   else
  347.     TextOut(PaintDC, 5, 5, '<No WAVEFORM file loaded>', 25);
  348.  
  349. { Beginning value }
  350.   TextOut (PaintDC, 50, 30, '0', 1);
  351.  
  352. { Ending number of samples }
  353.   if WaveLength <> 0 then
  354.     Str(WaveLength * WaveRatio, Buffer)
  355.   else
  356.     StrCopy(Buffer, 'Unknown');
  357.   TextOut(PaintDC, 325, 30, Buffer, StrLen(Buffer));
  358. end;
  359.  
  360. { Redefines GetWindowClass to give this application an icon and a menu.
  361. }
  362. procedure TSoundWindow.GetWindowClass(var AWndClass: TWndClass);
  363. begin
  364.   TWindow.GetWindowClass(AWndClass);
  365.   AWndClass.lpszMenuName := PChar(id_Menu);
  366. end;
  367.  
  368. { Returns the class name of this window.  This is necessary since we
  369.   redefine the inherited GetWindowClass method, above.
  370. }
  371. function TSoundWindow.GetClassName: PChar;
  372. begin
  373.   GetClassName := 'SoundPlay';
  374. end;
  375.  
  376. { Completes the initialization of the Window, performing
  377.   those functions which require a valid window handle. 
  378. }
  379. procedure TSoundWindow.SetupWindow;
  380. begin
  381.   TWindow.SetupWindow;
  382.   if WaveOutGetNumDevs = 0 then
  383.   begin
  384.     MessageBox(HWindow, 'No Wave Output device is available', 'Sound Demo',
  385.       mb_OK or mb_IconStop);
  386.     PostQuitMessage(0);
  387.   end;
  388. end;
  389.  
  390. { Obtains information about the system's sound generating capabilities.
  391. }
  392. procedure TSoundWindow.GetDeviceInfo;
  393. var
  394.   WOutCaps: TWaveOutCaps;
  395. begin
  396.   if WaveOutGetDevCaps(DeviceID, @WOutCaps, SizeOf(WOutCaps)) <> 0 then
  397.     MessageBox(HWindow, 'GetDevCaps Error', 'Sound Demo', mb_OK);
  398. end;
  399.  
  400.  
  401. { Plays the wave on request.
  402. }
  403. procedure TSoundWindow.CMSoundPlay(var Msg: TMessage);
  404. var
  405.   MyMenu : HMenu;
  406.   Res    : Longint;
  407.   ErrMsg : array [0..255] of Char;
  408. begin
  409.   if not IsRunning then
  410.   begin
  411. { MCI APIs to open a device and play a .WAV file, using notification to close
  412. }
  413.     MciOpenParm.dwCallback := 0;
  414.     MciOpenParm.wDeviceID  := 0;
  415.     MciOpenParm.wReserved0 := 0;
  416.     MciOpenParm.lpstrDeviceType  := nil;
  417.     MciOpenParm.lpstrElementName := ElementName;
  418.     MciOpenParm.lpstrAlias       := nil;
  419.  
  420.     if mciSendCommand(0, mci_Open, (mci_Wait or mci_Open_Element),
  421.         Longint(@MciOpenParm)) <> 0 then
  422.       MessageBox(HWindow, 'Open Error - A waveForm output device is ' +
  423.           'necessary to use this demo.', 'Sound Demo', mb_OK)
  424.     else
  425.     begin
  426.       DeviceID := MciOpenParm.wDeviceID;
  427.  
  428. { The time format in this demo is in Samples.
  429. }
  430.       MciSetParm.dwCallback   := 0;
  431.       MciSetParm.dwTimeFormat := mci_Format_Samples;
  432.       if mciSendCommand(DeviceID, mci_Set, mci_Set_Time_Format,
  433.       Longint(@MciSetParm)) <> 0 then
  434.       begin
  435.         StopWave;
  436.     MessageBox(HWindow, 'SetTime Error', 'Sound Demo', mb_OK)
  437.       end
  438.       else
  439.       begin
  440.         MciPlayParm.dwCallback := HWindow;
  441.         MciPlayParm.dwFrom     := 0;
  442.         MciPlayParm.dwTo       := 0;
  443.  
  444.     Res := mciSendCommand(DeviceID, mci_Play, mci_Notify,
  445.       Longint(@MciPlayParm));
  446.         if Res <> 0 then
  447.         begin
  448.           mciGetErrorString(Res, ErrMsg, SizeOf(ErrMsg));
  449.       MessageBox(HWindow, ErrMsg, 'Sound Demo', mb_OK or mb_IconStop);
  450.           StopWave;
  451.         end
  452.         else
  453.         begin
  454. { Modify the menu to toggle PLAY to STOP, and enable PAUSE.
  455. }
  456.           MyMenu := GetMenu(HWindow);
  457.           ModifyMenu(MyMenu, cm_SoundPlay, mf_String, cm_SoundPlay, '&Stop');
  458.       EnableMenuItem(MyMenu, cm_SoundPause, mf_Enabled);
  459.  
  460. { Make sure the Play/Stop toggle menu knows we're running.
  461. }
  462.       IsRunning := True; 
  463.  
  464. { Start a timer to show our progress through the waveform file.
  465. }
  466.           TimerGoing := (SetTimer(HWindow, Timer_Id, 500, nil) <> 0);
  467.  
  468. { Give enough information to the scrollbar to monitor the progress and issue a re-mci_Open.
  469. }
  470.           SoundBar^.ScrollSetName(ElementName);
  471.         end;
  472.       end;
  473.     end;
  474.   end
  475.   else
  476.   begin
  477. { Stop menu is toggled so kill the timer and stop the wave.
  478. }
  479.     KillTimer(HWindow, Timer_Id);
  480.     StopWave;
  481.   end;
  482. end;
  483.  
  484. { Pauses or resumes the playback in response to requests to do so from
  485.   the menu.  The File | Pause selection acts as a toggle.
  486. }
  487. procedure TSoundWindow.CMSoundPause(var Msg: TMessage);
  488. var
  489.   MyMenu: HMenu;
  490. begin
  491.   MyMenu := GetMenu(HWindow);
  492.  
  493.   if not Paused then
  494.   begin       { Pause the playing. }
  495.     MciGenParm.dwCallback := 0;
  496.     mciSendCommand(DeviceID, mci_Pause, mci_Wait, Longint(@MciGenParm));
  497.  
  498.     ModifyMenu(MyMenu, cm_SoundPause, mf_String, cm_SoundPause,
  499.       '&Resume'^I'Ctrl+P');
  500.   end
  501.   else
  502.   begin       { Resume the playing. }
  503.     MciGenParm.dwCallback := 0;
  504.     mciSendCommand(DeviceID, mci_Resume, mci_Wait, Longint(@MciGenParm));
  505.  
  506.     ModifyMenu(MyMenu, cm_SoundPause, mf_String, cm_SoundPause,
  507.       'P&ause'^I'Ctrl+P');
  508.   end;
  509.  
  510.   Paused := not Paused;
  511. end;
  512.  
  513. { Posts the About Box for the Sound Demo.
  514. }
  515. procedure TSoundWindow.CMHelpAbout(var Msg: TMessage);
  516. begin
  517.   Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
  518. end;
  519.  
  520. { Stops the playing waveform file, and closes the waveform device.
  521. }
  522. procedure TSoundWindow.StopWave;
  523. var
  524.   MyMenu: HMenu;
  525. begin
  526.   if DeviceID <> 0 then
  527.   begin
  528.     MciGenParm.dwCallback := 0;
  529.     mciSendCommand(DeviceID, mci_Stop,  mci_Wait, Longint(@MciGenParm));
  530.     mciSendCommand(DeviceID, mci_Close, mci_Wait, Longint(@MciGenParm));
  531.  
  532. { Reset the menus to Play menu and gray the Pause menu.
  533. }
  534.     MyMenu := GetMenu(HWindow);
  535.     ModifyMenu(MyMenu, cm_SoundPlay,  mf_String, cm_SoundPlay,
  536.       '&Play'^I'Ctrl+P');
  537.     ModifyMenu(MyMenu, cm_SoundPause, mf_String or mf_Grayed, cm_SoundPause,
  538.       'P&ause'^I'Ctrl+A');
  539.  
  540.     IsRunning := FALSE;
  541.     DeviceID  := 0;
  542.   end;
  543. end;
  544.  
  545. { Posts the file open dialog, gets a wave file name, and updates the sound
  546.   window to use it.
  547. }
  548. procedure TSoundWindow.CMFileOpen(var Msg: TMessage);
  549. const
  550.   DefExt = 'wav';
  551. var
  552.   OpenFN   : TOpenFileName;
  553.   Filter   : array [0..100] of Char;
  554.   FileName : TFilename;
  555.   WinDir   : array [0..145] of Char;
  556.   MyMenu   : HMenu;
  557. begin
  558.   GetWindowsDirectory(WinDir, SizeOf(WinDir));
  559.   SetCurDir(WinDir);
  560.   StrCopy(FileName, '');
  561.  
  562. { Set up a filter buffer to look for Wave files only.  Recall that filter
  563.   buffer is a set of string pairs, with the last one terminated by a
  564.   double-null.
  565. }
  566.   FillChar(Filter, SizeOf(Filter), #0);  { Set up for double null at end }
  567.   StrCopy(Filter, 'Wave Files');
  568.   StrCopy(@Filter[StrLen(Filter)+1], '*.wav');
  569.  
  570.   FillChar(OpenFN, SizeOf(TOpenFileName), #0);
  571.  
  572.   with OpenFN do
  573.   begin
  574.     hInstance     := HInstance;
  575.     hwndOwner     := HWindow;
  576.     lpstrDefExt   := DefExt;
  577.     lpstrFile     := ElementName;
  578.     lpstrFilter   := Filter;
  579.     lpstrFileTitle:= nil;     {Title not needed right now ... use full path }
  580.     flags         := ofn_FileMustExist;
  581.     lStructSize   := SizeOf(TOpenFileName);
  582.     nFilterIndex  := 1;       {Use first Filter String in lpstrFilter}
  583.     nMaxFile      := SizeOf(FileName);
  584.   end;
  585. { If a file is selected, turn the Play menu on, and update the sound
  586.   window to show the new file name.
  587. }
  588.   if GetOpenFileName(OpenFN) then
  589.   begin
  590.     MyMenu := GetMenu(HWindow);
  591.     EnableMenuItem(MyMenu, cm_SoundPlay, mf_Enabled);
  592.  
  593.     WaveLength := 0;
  594.     WaveRatio  := 0;
  595.     UpdateSoundWindow;
  596.   end;
  597. end;
  598.  
  599. { Responds to mm_MCINotify messages when mci_Play is complete.  If the
  600.   Stop/Close is from the thumb movement, then ignore it.  Otherwise,
  601.   kill the timer and reset the scroller.
  602. }
  603. procedure TSoundWindow.MciNotify(var Msg: TMessage);
  604. var
  605.   LoVal, HiVal: Integer;
  606. begin
  607.   if not FlushNotify then
  608.   begin               { Internal STOP/CLOSE, from thumb re-pos? }
  609.     if TimerGoing then
  610.     begin               { No, normal close. }
  611.       KillTimer(HWindow, Timer_Id);
  612. { Make sure the thumb is at the end. There could be some wm_Timer
  613.   messages on the queue when we kill it, thereby flushing wm_Timer's
  614.   from the message queue.
  615. }
  616.       SoundBar^.GetRange(LoVal, HiVal);
  617.       SoundBar^.SetPosition(HiVal);
  618.     end;
  619.  
  620.     StopWave;
  621.   end;
  622.   FlushNotify := False;  { Yes, so ignore the close. }
  623. end;
  624.  
  625. { Invalidates the client area of the Sound Window so that the
  626.   information display will get updated.
  627. }
  628. procedure TSoundWindow.UpdateSoundWindow;
  629. begin
  630.   InvalidateRect(HWindow, nil, True);
  631. end;
  632.  
  633. { Processes wm_Timer events.
  634. }
  635. procedure TSoundWindow.WMIdleStuff(var Msg: TMessage);
  636. begin
  637.   if not FlushNotify then
  638.   begin            { Internal STOP/CLOSE, from thumb re-pos? }
  639.     MciStatusParm.dwCallback := 0;     { No, normal close. }
  640.     MciStatusParm.dwItem     := mci_Status_Length;
  641.     mciSendCommand(DeviceID, mci_Status, mci_Status_Item,
  642.       Longint(@MciStatusParm));
  643.  
  644. { If the wavelength has changed, update the scroll bar numbers.
  645. }
  646.     if WaveLength <> MciStatusParm.dwReturn then
  647.     begin
  648.       UpdateSoundWindow;
  649.       WaveLength := MciStatusParm.dwReturn;
  650.     end;
  651.  
  652. { Compute the length and ratio and update SoundBar info.
  653. }
  654.     WaveRatio := Round((WaveLength / 32000) + 0.5);
  655.     SoundBar^.ScrollSetInfo(WaveRatio, WaveLength);
  656.     SoundBar^.SetRange(0, Round(WaveLength / WaveRatio));
  657.  
  658. { Update the current position.
  659. }
  660.     MciStatusParm.dwCallback := 0;
  661.     MciStatusParm.dwItem     := mci_Status_Position;
  662.     mciSendCommand(DeviceID, mci_Status, mci_Status_Item,
  663.       Longint(@MciStatusParm));
  664.  
  665.     SoundBar^.SetPosition(Round(MciStatusParm.dwReturn / WaveRatio));
  666.   end;
  667.  
  668.   FlushNotify := False;   { Yes, ignore this close. }
  669. end;
  670.  
  671.  
  672. { TDragApp Methods }
  673.  
  674. { Creates the application's main window.
  675. }
  676. procedure TSoundApp.InitMainWindow;
  677. begin
  678.   MainWindow := New(PSoundWindow, Init(nil, Application^.Name));
  679. end;
  680.  
  681. { Initializes this instance of the Sound Application.  Redefined
  682.   to load the accelerators.
  683. }
  684. procedure TSoundApp.InitInstance;
  685. begin
  686.   TApplication.InitInstance;
  687.   HAccTable := LoadAccelerators(HInstance, 'ACCELERATORS_1');
  688. end;
  689.  
  690.  
  691. { Main Program }
  692.  
  693. begin
  694.   App.Init(DemoTitle);
  695.   App.Run;
  696.   App.Done;
  697. end.
  698.  
  699.